<?php

/** Dataxi kone - main functions
 *  This collection of functions (libary) is licensed under GPL2.
 *  See LICENSE or www.gnu.org for more details.
 *
 *  @author: JQ + Henri Ranki 11.6.2001
 *
 *  $Id: dzkone.php 595 2006-04-03 16:00:47Z jyry $
 */

/* Include files needed */
#include_once("xmlrpc.inc");
#include_once("xmlrpcs.inc");
include_once("sodbseqkey.php");
include_once("sodebug.php");
include_once("sosqlhi.php");
include_once("sostring.php");
include_once("soset.php");
include_once("sodblock.php");
include_once("sogeneric.php");
include_once("dzdbgeneric.php");
include_once("dzpurkki.php");
include_once("dzstruct.php");
include_once("dzuser.php");
include_once("dzsesam.php");
include_once("dzitu.php");
include_once("dzconst.php");
include_once("dzrpc.php");

include("sodebug.inc");


/** xmlrpc handler.
 * This function receives rpc calls directed to kone. Create Kone object
 * and pass the request for it. Then return the result that kone produced.
 * Jarva is is the entry point for remote procedure calls.
 * @param array $Params An array of xmlrpcval objects from the client
 * @param string xmlrpc result packet
 */
function dzRpcKone($Params) {
    global $xmlrpcerruser; /* import user errcode value */

    /* Get the itu that was sent as parameter. */
    $itu = decodeRpcParameter($Params);

    /* Create new dzKone to handle the request */
    $kone = new dzKone();
    $result_itu = $kone->request($itu);

    /* Encode the result */
    $result_packet = encodeRpcResult($result_itu);

    return $result_packet;
} // dzRpcKone


/** Class to handle database access: query/read/write
 *  dzKone is used to access by using request string, array or dzitu in order
 *  to query database, read or write a set
 */
class dzKone {

    var $__Struct;       /** dzStruct Dataz structure */
    var $__Purkki;       /** dzPurkki Data stores TISKI and TAFFEL */
    var $__Soda;         /** soDa Data access miracle component */
    var $__Aliases;      /** Database aliases, returned by $this->getDbAlias */
    var $__SysDbAlias;   /** System db alias name */
    var $__Lock;         /** soDbRowLock Object to handle row lockings */
    var $__SeqKey;       /** soSeqKey Object to handle sequential keys */
    var $__InItu;        /** dzItu Itu that was received from the client.
                          *  This is only read */
    var $__OutItu;       /** dzItu Itu that holds the result for the client
                          *  This is sent to client as a result from Kone.
                          *  Only insert information to this Itu. */
    var $__SessionCheck; /** As long as the same instance of dzKone is running,
                          *  and user check ttl has not yet passed,
                          *  a valid user/session check time stamp is put here
                          *  and further queries to user db are omited. */

    /** Constructor
     */
    function dzKone() {
        $this->__Purkki = new dzPurkki();
        $this->__Struct = NULL;
        $this->__Soda = NULL;
        $this->__DbObjs = array();
        $this->__Lock = NULL;
        $this->__SeqKey = NULL;
        /* create result itu */
        $this->__OutItu = new dzItu();
    } // dzKone


    /** DB object caching function.
     *  Returns PEAR::DB object from cache or creates new one.
     *  @param string $DbAlias Alias to get DB object for.
     *  @return object PEAR::DB object.
     */
    function getDbAlias($DbAlias) {
        $DbAlias =  dzGetDbAliasName($DbAlias);

        $result = &$this->__Aliases[$DbAlias];
        if (!$result) {
            $result = &dzGetDbAlias($DbAlias);
            $this->__Aliases[$DbAlias] = &$result;
        }; // if

        return $result;
    } // getDbAlias


    /** Sets up database access connection.
     *  @param string $DbAlias Database alias name to connect to
     */
    function connect($DbAlias) {
        soDebug("dzKone->connect", SOD_DET);

        $sysdbaccess = &$this->getDbAlias($this->__SysDbAlias);

        $this->__Lock = new soDbRowLock($sysdbaccess[DZDBA_OBJ],
                                        $sysdbaccess[DZDBA_OPTIONS]);
        $this->__SeqKey = new soDbSeqKey($sysdbaccess[DZDBA_OBJ],
                                         $sysdbaccess[DZDBA_OPTIONS]);

        if ($DbAlias) {
            $dbaccess = &$this->getDbAlias($DbAlias);
            $this->__Soda = new soDa($dbaccess[DZDBA_OBJ],
                                     $dbaccess[DZDBA_OPTIONS]);
            if ($this->__Struct) $dbtt = $this->__Struct->getDBTT();
            if ($dbtt) {
                $dbtt = "file://".dzGetIniPath(DZID_INI_FOLDER)."/".
                        $dbtt.".dtt";
                soDebug("dzKone->connect: using dbtt: '".$dbtt."'", SOD_DET);
            };

            $this->__Soda->setupTranslations($dbtt);
        }; // if
    } // connect


    /** Disconnects all db connections
     */
    function disconnect() {
        $keys = array_keys($this->__Aliases);
        $i = count($keys);
        while (--$i >= 0) {
            $dbaccess = &$this->__Aliases[$keys[$i]];
            soDbDisconnect($dbaccess[DZDBA_OBJ]);
        }; // while
    } // disconnect


    /** Processes a request string.
     *  Decodes received url decoded string and passes created Itu forward
     *  to request handler.
     *  >$RequestStr:str URL encoded string to process
     *  <:str Result Itu in encoded string
     */
    function _requestStr($RequestStr) {
        $itu = new dzItu($RequestStr);
        $this->request($itu);
        return $this->__OutItu->encode();
    } // _requestStr


    /** Deals with the incoming Itu.
     *  Takes the payload and control data from the Itu and stores
     *  them. Then calls request handler.
     *  The Itu was sent by dzLaituri whether usin RPC or local call to dzKone.
     *  >$Itu:dzItu Itu that contains the data and control data
     *  <:dzItu Result from dzKone
     */
    function request(&$Itu) {
        /* Store the received TISKI in the Purkki */
        $this->__Purkki->setStore(TISKI, $Itu->getBlock(DZI_DATA));

        /* Payload data can be discarted, but store the control data. */
        $Itu->discardPayload();
        $this->__InItu = $Itu;

        /* Finally take care of the request. */
        $this->__serve();

        return $this->__OutItu;
    } // request


    /** Processes a requests
     *  Processes the request stored in __InItu and Purkki. Purkki contains
     *  parameters (data) and __InItu contains the control block. Please
     *  see more details in the introduction of this library.
     */
    function __serve() {
        include("sodebug.inc");

        /* Set the debug file and level specified in the ini file for Kone
           Here we use strange looking trick, but yes everythig is ok.
           Debug level must be set to 0 first to stop printing direct
           to output stream. xmlrpc would not like it. */
        $soDebugLevel = 0;
        $soDebugLogFile = dzGetIniItem("debug", "file", "kone", "ini");
        $soDebugLevel = intval(dzGetIniItem("debug", "level", "kone", "ini"));

        /* request type */
        $type = $this->__InItu->getBlockItemAt(DZI_CONTROL, DZK_REQUEST);
        if (!in_array($type, array(DZK_REQ_OPEN_SES,
                                   DZK_REQ_SES_LIST,
                                   DZK_REQ_CLOSE_SES,
                                   DZK_UPD_ACCESS_TIME,
                                   DZK_REQ_USER_INFO,
                                   DZK_REQ_CHECK_USER))) {

            $struct = $this->__InItu->getBlockItemAt(DZI_CONTROL, DZK_STRUCT);
            $stype = soGetStreamType($struct);
            if ($stype) list($stype, $struct) = soSplitStr($struct, "://", 1);
            soDebug("dzKone->__serve: Struct type is '$stype'", SOD_DET);
            switch ($stype) {
                case "file":
                    $paths = dzGetSourceCachePaths($struct);
                    $struct = soFileToString($paths["struct"]);
                case "object":
                    $this->__Struct = unserialize($struct);
                    break;
                default: // String source - setup struct object
                    $struct = str_replace("\r\n", "\n", $struct);
                    $struct = str_replace("\r", "\n", $struct);
                    $struct = str_replace(" ", "\n", $struct);
                    $this->__Struct = new dzStruct();
                    $this->__Struct->setup($struct);
            }; // switch
        }; // if

        /* Sort out dbalias & sysdbalias */
        $dbalias = $this->__InItu->getBlockItemAt(DZI_CONTROL, DZK_DATABASE);
        if (!$dbalias && $this->__Struct) {
            $dbalias = $this->__Struct->getDbAlias();
        }; // if
        if (!$dbalias) {
            $dbalias = dzGetIniItem(DZIS_GENERAL, DZID_DBALIAS, "app");
        }; // if
        $this->__SysDbAlias = dzGetIniItem(DZIS_GENERAL,
                                           DZID_SYSDBALIAS, "app");
        if (!$this->__SysDbAlias) $this->__SysDbAlias = $dbalias;
        $this->connect($dbalias);

        soDebug("dzKone: Processing '$type' request", SOD_LO);
        $this->renameAliases(TISKI);
        switch ($type) {
            case DZK_REQ_FETCH:
                include_once("dzkone_fetch.php");
                $result = __dzkone__fetch($this);
                $store_id = TAFFEL;
                break;

            case DZK_REQ_QUERY:
                include_once("dzkone_query.php");
                $result = __dzkone__query($this);
                $store_id = TAFFEL;
                break;

            case DZK_REQ_LIST:
                include_once("dzkone_list.php");
                $result = __dzkone__list($this, 0);
                $store_id = TAFFEL;
                break;

            case DZK_REQ_FRESHLIST:
                include_once("dzkone_list.php");
                $result = __dzkone__list($this, 1);
                $store_id = TAFFEL;
                break;

            case DZK_REQ_READ:
                include_once("dzkone_read.php");
                $result = __dzkone__readSet($this);
                $store_id = TAFFEL;
                break;

            case DZK_REQ_WRITE:
                include_once("dzkone_write.php");
                $result = (__dzkone__writeSet($this) >= 0);
                $store_id = TISKI;
                break;

            case DZK_REQ_DELETE:
                include_once("dzkone_delete.php");
                $result = (__dzkone__deleteSet($this) != -1);
                $store_id = NULL;
                break;

            case DZK_REQ_LOCK:
                include_once("dzkone_read.php");
                $result = __dzkone__readSet($this, 1);
                $store_id = TAFFEL;
                break;

            case DZK_REQ_UNLOCK:
                include_once("dzkone_lock.php");
                $result = __dzkone__lockSet($this, SOLM_UNLOCK,
                    $this->__getCacheFileName($this->__getMasterKeyVals()));
                $store_id = TAFFEL;
                break;

            case DZK_REQ_OPEN_SES:
                /* If session index were given an old session will be opened */
                $ses_ind = $this->__InItu->getBlockItemAt(DZI_CONTROL,
                                                          DZK_SESSION_INDEX);
                /* if there were no session idex create a new proces. */
                if ($ses_ind < 0)
                    $this->__processSessionRequest();
                else
                    $this->__openSession($ses_ind);

                $result = 1;
                $store_id = NULL;
                break;

            case DZK_REQ_SES_LIST:
                $this->__getSessionList();
                $result = 1;
                $store_id = NULL;
                break;

            case DZK_REQ_CLOSE_SES:
                $this->__closeSession();
                $result = 1;
                $store_id = NULL;
                break;

            case DZK_UPD_ACCESS_TIME:
                $this->__updAccessTime();
                $result = 1;
                $store_id = NULL;
                break;

            case DZK_REQ_USER_INFO:
                /* create the request string. this request returns always
                   all possible user information. With the other requests
                   the needed information must be explicitely defined. */
                $request_str = implode(array(DZU_LEVEL, DZU_LANG,
                                             DZU_FULLNAME, DZU_MAXSESSIONS,
                                             DZU_GROUPS), ";");
                $this->__OutItu->setBlockItem(DZI_CONTROL, DZK_RESULT, DZR_OK);
                $result = 1;
                $store_id = NULL;
                break;

            case DZK_REQ_CHECK_USER:
                $this->__verifyUserSession();
                $result = 1;
                $store_id = NULL;
                break;

            default: // unknown request
                soDebug("dzKone: Very strange and unknown request '$type' ".
                        "skipped", SOD_LO);
                $this->__OutItu->setBlockItem(DZI_CONTROL,
                                              DZK_RESULT, DZR_ERROR);
         }; // switch

        /* If all right, add requested information to result Itu */
        if (!$result) {
            $store_id = NULL;
        } else {
            $user_id = $this->__InItu->getBlockItemAt(DZI_CONTROL, DZK_USER);
            $this->__includeInfo($user_id, $request_str);
        };
        /* Create the result Itu */
        $this->__buildItu($store_id);
        $this->disconnect();
    } // __serve


    /** Sets the data block in the result Itu
     *  @param NULL|TISKI|TAFFEL $StoreID Store to merge with control data.
     *      NULL=only control data.
     */
    function __buildItu($StoreID = NULL) {
        if ($StoreID != NULL) {
            if (!$this->__Purkki->isStore($StoreID)) return NULL; // <-- EXIT
            $value = $this->__Purkki->getStore($StoreID);
            $this->__OutItu->setBlock(DZI_DATA, $value);
        };
    } // __buildItu


    /** Include requested information to result Itu
     *  The client can ask some information to be returned along the result Itu
     *  this is usualy user information like user's lanquage and level.
     *  Requested information can be given as parameter or it is tried to
     *  extract from request Itu.
     *  @param string $UserId The user name whose information is requested
     *  @param string $RequestStr specify the information to request
     */
    function __includeInfo($UserId, $RequestStr = "") {
        /* get the list of the requested data. If it was not given as parameter
           search it from request Itu */
        if (!$RequestStr) {
            $RequestStr = $this->__InItu->getBlockItemAt(DZI_CONTROL,
                                                         DZK_INFO);
        } // if ($RequestStr)
        soDebug("dzKone: Requested info '$RequestStr'", SOD_DET);

        $info_list = soExplode($RequestStr, ";");
        $sysdbaccess = &$this->getDbAlias($this->__SysDbAlias);
        $user = new dzUser($sysdbaccess[DZDBA_OBJ],
                           $sysdbaccess[DZDBA_OPTIONS]);
        $result = &$user->getInfo($info_list, $UserId);

        foreach ($info_list as $info) {
            $this->__OutItu->setBlockItem(DZI_CONTROL, $info, $result[$info]);
        }; // foreach
    } // __includeInfo


    /** update the given sessions access time
     */
    function __updAccessTime() {
        soDebug("dzKone: Entered __updAccessTime()", SOD_DET);
        $session_id = $this->__InItu->getBlockItemAt(DZI_CONTROL, DZK_SESSION);

        /* Create dzSesam object that is used for session management */
        $sysdbaccess = &$this->getDbAlias($this->__SysDbAlias);
        $ses_adm = new dzSesam($sysdbaccess[DZDBA_OBJ],
                               $sysdbaccess[DZDBA_OPTIONS]);

        /* Try to update the session's acess time */
        if ($ses_adm->updAccessTime($session_id)) {
            soDebug("dzKone: Acess time updated. Session id=$session_id",
                     SOD_DET);
            $this->__OutItu->setBlockItem(DZI_CONTROL, DZK_RESULT, DZR_OK);
        } else {
            soDebug("dzKone: Error updating session access time $session_id",
                     SOD_HI_E);
            $this->__OutItu->setBlockItem(DZI_CONTROL, DZK_RESULT, DZK_ERROR);
        } // if
    } // __updAccessTime


    /** Get the list of open session for a user
     */
    function __getSessionList() {
        soDebug("dzKone: Entered __getSessionList()", SOD_DET);
        /* Find out the open sessions for the user. */
        $user_id = $this->__InItu->getBlockItemAt(DZI_CONTROL, DZK_USER);
        $sysdbaccess = &$this->getDbAlias($this->__SysDbAlias);
        $ses_adm = new dzSesam($sysdbaccess[DZDBA_OBJ],
                               $sysdbaccess[DZDBA_OPTIONS]);
        $user_ses = $ses_adm->getSessions($user_id);
        $ses_count = count($user_ses);
        soDebug("dzKone: $user_id has $ses_count open sessions", SOD_DET);

        /* Change session data to semi-colon separated string */
        for ($c = 0; $c < $ses_count; $c++) {
            $user_ses[$c] = implode($user_ses[$c], ";");
        } // for

        $this->__OutItu->setBlockItem(DZI_CONTROL, DZK_RESULT, $ses_count);
        $this->__OutItu->setBlockItem(DZI_CONTROL, DZK_SESSION_LIST, 
                                      $user_ses);
    } // __getSessionList


    /** Create new or open existing session
     */
    function __processSessionRequest() {
        soDebug("dzKone: Entered __processSessionRequest()", SOD_DET);
        $user_id = $this->__InItu->getBlockItemAt(DZI_CONTROL, DZK_USER);

        /* Create dzSesam object that is used for session management */
        $sysdbaccess = &$this->getDbAlias($this->__SysDbAlias);
        $ses_adm = new dzSesam($sysdbaccess[DZDBA_OBJ],
                               $sysdbaccess[DZDBA_OPTIONS]);
        /* Try to create a new session */
        $session_id = $ses_adm->addSession($user_id);
        soDebug("dzKone: Created a new session for $user_id with id ".
                $session_id, SOD_DET);

        $this->__OutItu->setBlockItem(DZI_CONTROL, DZK_RESULT, DZR_OK);
        $this->__OutItu->setBlockItem(DZI_CONTROL, DZK_SESSION_ID,
                                      $session_id);
    } // __processSessionRequest


    /** Get an old session id
     * Returns the session id based on the index number in the session table.
     * When user chooses a session from the session list only the index
     * number is returned to Dataxi.
     * >$SesIndex:int   Session index
     */
    function __openSession($SesIndex) {
        soDebug("dzKone: Entered __openSession()", SOD_DET);
        $user_id = $this->__InItu->getBlockItemAt(DZI_CONTROL, DZK_USER);

        /* Create dzSesam object that is used for session management */
        $sysdbaccess = &$this->getDbAlias($this->__SysDbAlias);
        $ses_adm = new dzSesam($sysdbaccess[DZDBA_OBJ],
                               $sysdbaccess[DZDBA_OPTIONS]);
        /* Try to create a new session */
        $session_list = $ses_adm->getSessions($user_id);
        $ses_count = count($session_list);
        /* check that there were enough sessions in the dzsessions table */
        if (count($session_list) >= $SesIndex) {
            /* Session id is in the third cell of the session info table */
            $session_id = $session_list[$SesIndex][2];
            soDebug("dzKone: Opened a session for $user_id with id $session_id",
                    SOD_DET);
            $this->__OutItu->setBlockItem(DZI_CONTROL,
                                          DZK_SESSION_ID,
                                          $session_id);
            $result = DZR_OK;
        } else {
            /* if there were an error and the old session */
            $result = DZR_SESSION_TABLE_ERROR;
        } // if (count($session_list))

        $this->__OutItu->setBlockItem(DZI_CONTROL, DZK_RESULT, $result);
    } // __openSession


    /** Remove session id from the session table
     */
    function __closeSession() {
        $session_id = $this->__InItu->getBlockItemAt(DZI_CONTROL, DZK_SESSION);

        /* Create dzSesam object that is used for session management */
        $sysdbaccess = &$this->getDbAlias($this->__SysDbAlias);
        $ses_adm = new dzSesam($sysdbaccess[DZDBA_OBJ],
                               $sysdbaccess[DZDBA_OPTIONS]);
        /* Try to remove the session */
        if ($ses_adm->delSession($session_id)) {
            soDebug("dzKone: Session '$session_id' removed", SOD_DET);
            $this->__OutItu->setBlockItem(DZI_CONTROL, DZK_RESULT, DZR_OK);
        } else {
            soDebug("dzKone: Error removing session $session_id", SOD_HI_E);
            $this->__OutItu->setBlockItem(DZI_CONTROL, DZK_RESULT, DZK_ERROR);
        } // if ($ses_adm->delSession($user_id))

        /* Remove locks */
        $this->__Lock->delSesLocks($session_id);
    } // __closeSession


    /** Sets lock parameters from DATA/CONTROL block
     *  Common routine used by __lockSet, __readSet ...
     */
    function __setLockParams() {
        $key_vals = $this->__getMasterKeyVals();
        $owner = $this->__InItu->getBlockItemAt(DZI_CONTROL, DZK_USER);
        $session_id = $this->__InItu->getBlockItemAt(DZI_CONTROL, DZK_SESSION);
        $this->__Lock->setParams($this->__Struct->getMaster("tablename"),
                                 $key_vals, $owner, $session_id);
    } // __setLockParams


    /** Get data/where/order cols for each table.
     *  Used by __writeSet, __query and __readSet.
     *  @param bool $Query Flag to indicate that the caller is __query.
     *  @return soSet/array/str Returns a set of tables each containing an 2-d
     *      array of all the data/where/order column names for each table.
     */
    function __getColsByTable($Query = 0) {
        $result = new soSet();

        /* 1. init result and setup data cols */
        $colnames = $this->__Struct->getColNames();
        foreach ($colnames as $colname) {

            /* v- [0] = table name, [1] = col name */
            $split = soSplitStr($colname, ".", 1, 0);
            if (!count($split)) return $result; // <-- EXIT

            if ($split[0] != DZPSEUDO) {
                $soda = $result->getItem($split[0]);
                if ($soda == NULL) {
                    /* new table to soda */
                    $soda = array();
                    $soda["datacols"] = array();
                    $soda["wherecols"] = array();
                    $soda["wherevals"] = array();
                    $soda["ordercols"] = array();
                    $soda["ordervals"] = array();
                }; // if

                /* The code below is not so nice as it should be, but php 4.0.6
                 * goes all tits up when done in any other way. */

                /* Key: 1=only key, 0=only data col, 2=both key/data col */
                $col = $this->__Struct->getCol($colname);
                if ($col->Key != 1) {
                    $datacols = &$soda["datacols"];
                    array_push($datacols, $colname.":".
                               $this->__Struct->getDatatype($colname));
                    $result->setItem($split[0], $soda);
                };
            }; // if != DZPSEUDO
        }; // foreach

        /* 2. setup where cols & vals and order cols */
        $mkeycount = $this->__Struct->getMaster("keycount");
        $mtable_name = $this->__Struct->getMaster("tablename");
        $tablenames = $result->getKeys();

        if (!$mtable_name) return $result; // <-- EXIT

        $mtable = $this->__Struct->getTable($mtable_name);
        $mkeynames = $mtable->KeyCols->getKeys();
        $tcount = count($tablenames);

        for ($t = 0; $t < $tcount; $t++) {
            $tablename = $tablenames[$t];
            $soda = $result->getItem($tablename);

            /* Current table and it's keys */
            $table = $this->__Struct->__Tables->getItem($tablename);
            $keynames = $table->KeyCols->getKeys();

            /* loop current table key cols */
            $icount = count($keynames);
            for ($i = 0; $i < $icount; $i++) {
                $col = $table->KeyCols->getItem($keynames[$i]);
                if ($i < $mkeycount) {
                    $column = $tablename.".".$keynames[$i];
                    $coltype = $column.":".
                               $this->__Struct->getDatatype($column);
                    $mcolumn = $mtable_name.".".$mkeynames[$i];
                    array_push($soda["wherecols"], $coltype);

                    $value = $this->__Purkki->getStoreItem(TISKI, $mcolumn, 0);
                    if (!$Query) {
                        $rcount =
                            $this->__getRowCount(TISKI, $tablename, "max");
                        for ($r = 0; $r < $rcount; $r++) {
                            $this->__Purkki->setStoreItem(TISKI, $column, $r,
                                                          $value);
                        }; // for
                    };
                    array_push($soda["wherevals"], $value);
                } else {
                    $datatype = $this->__Struct->getDatatype($tablename.".".
                                                             $keynames[$i]);
                    array_push($soda["ordercols"],
                               $tablename.".".$keynames[$i].":".$datatype);
                    $soda["ordervals"] = $this->__Purkki->getStoreCol(TISKI,
                                         $tablename.".".$keynames[$i].
                                         ":".$datatype);
                }; // else if keypos < mkeycount
            }; // for $i
            $result->setItem($tablename, $soda);
        }; // for $t
        return $result;
    } // __getColsByTable


    /** Gets min or max row count for a table.
     *  Loops through all column in purkki of specified table and returns
     *  minimum or maximum number of rows found.
     *  @param string $StoreId Store (TISKI/TAFFEL) to use in search.
     *  @param string $TableName Table name.
     *  @param string $MinMax "min"=get the min. rowcount, "max"=get the max
     *  @param int $ColType Include (0) data cols, (1) key cols or (2) all
     *  cols in the table.
     *  @return int Returns max number of elements found in table columns.
     */
    function __getRowCount($StoreId, $TableName, 
                           $MinMax = "max", $ColType = 2) {
        $result = 0;
        $colnames = $this->__Struct->__Cols->getKeys();
        foreach ($colnames as $colname) {
            /* v- [0] = table name, [1] = col name */
            $split = soSplitStr($colname, ".", 1, 0);
            if ($is_key = ($this->__Struct->isKeyCol($colname) >= 0)) {
                $col = $this->__Struct->getCol($colname);
                $is_data = ($col->Key != 1);
            } else {
                $is_data = 1;
            };
            if (($split[0] == $TableName) && 
                (($ColType == 2) || 
                 ($is_data && ($ColType != 1)) ||
                 ($is_key && $ColType))) {
                $arr_count =
                    soArrayCount($this->__Purkki->getStoreCol($StoreId,
                                                              $colname));
                if ($MinMax == "max") {
                    $result = max($result, $arr_count);
                } else {
                    $result = min($result, $arr_count);
                };
            }; // if
        }; // foreach

        return $result;
    } // __getRowCount


    /** Returns key for master table.
     *  @return array/any Key values for master table or NULL on error.
     */
    function __getMasterKeyVals() {
        $tables = $this->__getColsByTable();
        $mtable_name = $this->__Struct->getMaster("tablename");
        $mtable = $tables->getItem($mtable_name);

        if ($tables->Error) {
            return NULL;
        } else {
            return $mtable["wherevals"];
        };
    } // __getMasterKeyVals


    /** Gets common filename part part for cache/list file.
     *  Returns common part for file names used by cache and list files.
     *  <:str File name start.
     */
    function __getFileName_common() {
        $folder = dzGetIniPath(DZID_SITE_TMP_FOLDER);
        $struct_name = $this->__Struct->_Name;
        if (!$struct_name) {
            $struct_name = $this->__Struct->getMaster("tablename");
        };

        return "file://".$folder."/dz".$struct_name."_";
    } // __getFileName_common


    /** Gets the file name for cache file.
     *  Returns the cache file name, that consists of commont file name
     *  start and key values
     *  >$Values:array/str Key values
     *  <:str Cache file name
     */
    function __getCacheFileName($Values) {
        $v = count($Values);
        while ((--$v >= 0) && (!$Values[$v]));

        /* All key values null -> new set -> no cache name -> no caching */
        if ($v < 0) {
            return ""; // <-- EXIT
        } else {
            return $this->__getFileName_common().
                urlencode(SO_URL_ARRAY_DELIM.
                implode(SO_URL_ARRAY_DELIM, $Values));
        }; // else if

        return $result;
    } // __getCacheFileName


    /** Gets the file name for browse list file.
     *  Returns the list file name, that consists of commont file name
     *  start and unique, random number.
     *  >$ArrSet:soArraySet Query condition set to use as the seed for list
     *      file name
     *  <:str List file name
     */
    function __getListFileName($ArrSet) {
        return $this->__getFileName_common().md5($ArrSet->dump());
    } // __getListFileName


    /** Check user validity.
     *  Sets return __OutItu results ok for user check if so.
     *  @param string UserID User's ID
     *  @return bool True if user check ok, otherwise false.
     */
    function __verifyUser($UserID) {
        $sysdbaccess = &$this->getDbAlias($this->__SysDbAlias);
        $user = new dzUser($sysdbaccess[DZDBA_OBJ],
                           $sysdbaccess[DZDBA_OPTIONS]);
        $result = $user->getInfo(array(DZU_PASSWORD,
                                       DZU_INACTIVE,
                                       DZU_LEVEL,
                                       DZU_MAXSESSIONS), $UserID);
        switch ($result[DZU_STATUS]) {
            case DZU_ERR_BADUSER:
                /* silly username */
                soDebug("dzKone: Unknown user", SOD_LO_E);
                $this->__OutItu->setBlockItem(DZI_CONTROL,
                                              DZK_RESULT,
                                              DZVU_UNKNOWN_USER);
                break;

            case DZU_ERR_QUERYERROR:
                $this->__OutItu->setBlockItem(DZI_CONTROL,
                                              DZK_RESULT,
                                              DZVU_USER_TABLE_CREATION_FAILED);
            default:
                /* Check password if requested so */
                $password = $this->__InItu->isBlockItem(DZI_CONTROL,
                                                        DZK_PASSWORD);
                if ($password) {
                    $password = $this->__InItu->getBlockItemAt(DZI_CONTROL,
                                                               DZK_PASSWORD);
                    $password = ($result[DZU_PASSWORD] == $password);
                } else {
                    $password = 1;
                }; // else if

                /* Got user's password from user database. Now check it */
                if ($password && (!$attr[DZU_INACTIVE])) {
                    soDebug("dzKone: User check ok", SOD_LO_E);
                    $this->__OutItu->setBlockItem(DZI_CONTROL,
                                                  DZK_RESULT,
                                                  DZVU_USER_CHECK_OK);
                    $result = 1;
                } else {
                    soDebug("dzKone->__verifyIUser: ".
                            "User authentication failed, ".
                            "password '(secret)' does not match", SOD_LO_E);
                    $this->__OutItu->setBlockItem(DZI_CONTROL,
                                                  DZK_RESULT,
                                                  DZVU_CHECK_FAILED);
                } // if $result
        }; // switch

        return $result;
    } // __verifyUser


    /** Verify user & session access rights fopr the request
     *  Checks for the user rights to given session. Also checks that
     *  password and user name matches.
     *  Parameters are took from the __InItu.
     *  This sets the __OutItu error if required.
     */
    function __verifyUserSession() {

        /* As long as this instance of dzKone is running and usercheck ttl
         * has not yet passed, and a valid user/session check has been
         * encountered, there is no need to go to the user db again. */
        $user_check_ttl = dzGetiniItem(DZIS_SYS, DZID_USER_TTL, "dataxi");
        if (($this->__SessionCheck +$user_check_ttl) > time()) {
            soDebug("dzKone->__verifyUserSession: cached check OK", SOD_DET);
            $this->__OutItu->setBlockItem(DZI_CONTROL,
                                          DZK_STATUS,
                                          DZVU_SESSION_OWNER);
            return; // <-- EXIT
        };

        /* get the user id & password and the session ID */
        $user_id = $this->__InItu->getBlockItemAt(DZI_CONTROL, DZK_USER);
        $session_id = intval($this->__InItu->getBlockItemAt(DZI_CONTROL, 
                                                            DZK_SESSION));
        soDebug("dzKone->__verifyUserSession: User check for '$user_id'. ".
                "Session Id $session_id", SOD_DET);
        $result = $this->__verifyUser($user_id);

        /* User check OK, check session */
        $sysdbaccess = &$this->getDbAlias($this->__SysDbAlias);
        $session_adm = new dzSesam($sysdbaccess[DZDBA_OBJ],
                                   $sysdbaccess[DZDBA_OPTIONS]);
        if (!$session_adm->isOwner($user_id, $session_id)) {

            /* find out the real owner */
            $real_owner = $session_adm->getSesOwner($session_id);

            /* get the levels for the real owner */
            $owner_level = $result[DZU_LEVEL];
            if ($owner_level < $result[DZU_LEVEL]) {
                soDebug("dzKone->__verifyUserSession: User '$user_id' took ".
                        "session over from user $real_owner", SOD_LO);
                $this->__OutItu->setBlockItem(DZI_CONTROL,
                                              DZK_STATUS,
                                              DZVU_CHECK_TAKE_OVER);
            } else {
                /* The user is not allowed tho take control over the
                   requested session */
                soDebug("dzKone->__verifyUserSession: User does not own ".
                        "the session", SOD_LO);
                $this->__OutItu->setBlockItem(DZI_CONTROL,
                                              DZK_STATUS,
                                              DZVU_CHECK_FAILED);
            }; // else if ($owner_level < $result[DZU_LEVEL])
        } else {
            /* User owns the session */
            soDebug("dzKone->__verifyUserSession: User owns the session",
                    SOD_LO);
            $this->__OutItu->setBlockItem(DZI_CONTROL,
                                          DZK_STATUS,
                                          DZVU_SESSION_OWNER);
            $this->__SessionCheck = time();
        }; // else if (!$SesAdm->isOwner($user_id, $session_id)
    } // __verifyUserSession


    /** Fixes alias table name in a soItu object.
     *  As import in dzItu only imports as is bases, this method fixes possibly
     *  wrong table names in such by replacing alias.colname with
     *  table.colname.
     *  @param TISKI/TAFFEL $StoreId Store ID to rename alias names in.
     *  @return int No. of renames done.
     */
    function renameAliases($StoreId) {
        $result = 0;

        /* Must access the actual itu directly. */
        $store = $this->__Purkki->getStore($StoreId);

        $keys = $store->getKeys();
        foreach ($keys as $key) {
            $name = $this->__Struct->findCol($key);
            if ($name) {
                $store->renameItem($key, $name);
                $result++;
            }; // if
        }; // foreach
        $this->__Purkki->setStore($StoreId, $store);

        return $result;
    } // renameAliases


} // class dzKone


?>
